package com.quanshi.shopping_search_api.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.FieldSuggester;
import co.elastic.clients.elasticsearch.core.search.Suggester;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.elasticsearch.indices.AnalyzeRequest;
import co.elastic.clients.elasticsearch.indices.AnalyzeResponse;
import co.elastic.clients.elasticsearch.indices.analyze.AnalyzeToken;
import co.elastic.clients.json.JsonData;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.quanshi.shopping_common.constant.Constant;
import com.quanshi.shopping_common.entity.*;
import com.quanshi.shopping_common.service.ISearchService;
import com.quanshi.shopping_search_api.repository.GoodsEsRepository;
import lombok.SneakyThrows;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Order;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@DubboService
@Service
public class SearchServiceImpl implements ISearchService {


    @Autowired
    private ElasticsearchClient client;

    @Autowired
    private GoodsEsRepository goodsEsRepository;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;


    @Override
    @SneakyThrows
    public List<String> autoSuggest(String keyword) {

        //创建补全条件对象
        Suggester suggester = Suggester.of(fn -> fn.suggesters("tags_suggestion",
                FieldSuggester.of(fs -> fs.text(keyword).completion(
                        cs -> cs.field("tags")
                                .skipDuplicates(true)
                                .size(10)))));

        //补全
        SearchResponse<Map> searchResponse = client.search(s -> s.index("goods").suggest(suggester), Map.class);

        List<Suggestion<Map>> suggestions = searchResponse.suggest().get("tags_suggestion");

        Suggestion<Map> mapSuggestion = suggestions.get(0);

        //获取补全的数据
        List<CompletionSuggestOption<Map>> options = mapSuggestion.completion().options();

        //补全的数据
        List<String> suggests = options.stream().map(CompletionSuggestOption::text).toList();

        return suggests;
    }

    @Override
    public GoodsSearchResult search(GoodsSearchParam goodsSearchParam) {
        // 1.构造ES搜索条件
        NativeQuery nativeQuery = this.buildQuery(goodsSearchParam);
        // 2.搜索
        SearchHits<GoodsES> searchHits = elasticsearchTemplate.search(nativeQuery, GoodsES.class);

        //当前页数据
        List<GoodsES> records = searchHits.getSearchHits().stream().map(SearchHit::getContent).toList();

        long totalHits = searchHits.getTotalHits();
        // 3.将查询结果封装为Page对象
        Page<GoodsES> page = new Page<>();
        page.setCurrent(goodsSearchParam.getPage());
        page.setSize(goodsSearchParam.getSize());
        page.setTotal(totalHits);
        page.setRecords(records);

        //创建搜索结果对象
        GoodsSearchResult goodsSearchResult = new GoodsSearchResult();

        //设置请求参数（回显）
        goodsSearchResult.setGoodsSearchParam(goodsSearchParam);

        //设置分页对象
        goodsSearchResult.setGoodsPage(page);

        //调用封装面板方法
        buildSearchPanel(goodsSearchResult,goodsSearchParam);

        return goodsSearchResult;

    }

    //封装搜索结果面板方法
    public void buildSearchPanel(GoodsSearchResult goodsSearchResult, GoodsSearchParam goodsSearchParam) {

        //设置查询面板参数永远取最前面第一页的20条数据
        goodsSearchParam.setPage(1);
        goodsSearchParam.setSize(20);
        goodsSearchParam.setSortFiled(null);
        goodsSearchParam.setSort(null);


        //面板的查询条件
        NativeQuery nativeQuery = buildQuery(goodsSearchParam);

        //面板的搜索结果
        SearchHits<GoodsES> searchHits = elasticsearchTemplate.search(nativeQuery, GoodsES.class);


        //处理搜索结果
        List<SearchHit<GoodsES>> searchHitList = searchHits.getSearchHits();


        //创建品牌集合
        Set<String> brands = new HashSet<>();
        //创建商品分类集合
        Set<String> productTypes = new HashSet<>();
        //商品规格集合
        Map<String, Set<String>> specificationsMap = new HashMap<>();

        for (SearchHit<GoodsES> goodsESSearchHit : searchHitList) {
            //获取搜索数据
            GoodsES goodsES = goodsESSearchHit.getContent();
            //添加品牌
            brands.add(goodsES.getBrand());
            //添加商品分类
            productTypes.addAll(goodsES.getProductType());


            //获取商品的规格
            Map<String, List<String>> specification = goodsES.getSpecification();

            if (CollUtil.isNotEmpty(specification)) {


                for (Map.Entry<String, List<String>> specMap : specification.entrySet()) {

                    String specName = specMap.getKey();// CPU ，屏幕尺寸

                    List<String> options = specMap.getValue();// ["5.5存","6.0寸"]

                    //如果没有这个key 第一此添加
                    if (!specificationsMap.containsKey(specName)) {
                        specificationsMap.put(specName, new HashSet<>(options));
                    } else {
                        //如果是非第一次添加规格项，将现有规格项取出来，添加以后，重新设置
                        specificationsMap.get(specName).addAll(options);
                    }
                }
            }
        }

        //设置查询结果品牌
        goodsSearchResult.setBrands(brands);
        //设置查询结果商品分类
        goodsSearchResult.setProductType(productTypes);
        //设置商品规格项
        goodsSearchResult.setSpecifications(specificationsMap);
    }


    /**
     * 构建搜索查询对象
     *
     * @param goodsSearchParam
     * @return
     */
    private NativeQuery buildQuery(GoodsSearchParam goodsSearchParam) {
        // 1.构造ES搜索条件
        NativeQueryBuilder nativeQueryBuilder = new NativeQueryBuilder();

        //创建bool
        BoolQuery.Builder boolbuilder = new BoolQuery.Builder();

        //判断是否有关键字
        String keyword = goodsSearchParam.getKeyword();
        if (StrUtil.isBlank(keyword)) {
            //查询所有
            MatchAllQuery.Builder builder = new MatchAllQuery.Builder();
            boolbuilder.must(builder.build()._toQuery());

        } else {
            //1.关键字查询
            //条件查询：goodsName，caption,brand
            MultiMatchQuery multiMatchQuery = MultiMatchQuery.of(fn -> fn.query(keyword).fields("goodsName", "caption", "brand"));
            boolbuilder.must(multiMatchQuery._toQuery());
        }

        //2.品牌查询
        String brand = goodsSearchParam.getBrand();
        if (StrUtil.isNotBlank(brand)) {
            TermQuery termQuery = TermQuery.of(fn -> fn.field("brand").value(brand));
            boolbuilder.must(termQuery._toQuery());
        }
        //3.商品的价格范围
        Double lowPrice = goodsSearchParam.getLowPrice();
        Double highPrice = goodsSearchParam.getHighPrice();
        if (Objects.nonNull(lowPrice) && lowPrice > 0) {
            RangeQuery rangeQuery = RangeQuery.of(fn -> fn.field("price").gte(JsonData.of(lowPrice)));
            boolbuilder.must(rangeQuery._toQuery());
        }

        if (Objects.nonNull(highPrice) && highPrice > 0) {
            RangeQuery rangeQuery = RangeQuery.of(fn -> fn.field("price").lte(JsonData.of(highPrice)));
            boolbuilder.must(rangeQuery._toQuery());
        }

        //4.规格查询
        Map<String, String> specificationOption = goodsSearchParam.getSpecificationOption();
        if (CollUtil.isNotEmpty(specificationOption)) {
            for (Map.Entry<String, String> map : specificationOption.entrySet()) {
                //商品规格名称
                String specName = map.getKey();//CPU  屏幕尺寸
                String optionName = map.getValue();//规格项  i5/i7   4.5/4.7

                TermQuery termQuery = TermQuery.of(fn -> fn.field("specification."+specName+".keyword").value(optionName));
                boolbuilder.must(termQuery._toQuery());
            }

        }

        //5. 新品或者价格排序  NEW PRICE  排序 ASC  DESC
        String sortFiled = goodsSearchParam.getSortFiled();//NEW PRICE
        String sort = goodsSearchParam.getSort();//ASC DESC
        if (StrUtil.isNotBlank(sortFiled) && StrUtil.isNotBlank(sort)) {

            //排序对象
            Sort.Order order = null;

            //新品升序
            if (Constant.NEW.equals(sortFiled) && Constant.ASC.equals(sort)) {
                order = Order.asc("id");
            }
            //新品降序
            if (Constant.NEW.equals(sortFiled) && Constant.DESC.equals(sort)) {
                order = Order.desc("id");
            }
            //价格升序
            if (Constant.PRICE.equals(sortFiled) && Constant.ASC.equals(sort)) {
                order = Order.asc("price");
            }
            //价格降序
            if (Constant.PRICE.equals(sortFiled) && Constant.DESC.equals(sort)) {
                order = Order.desc("price");
            }

            //添加排序
            nativeQueryBuilder.withSort(Sort.by(order));
        }
        //6.分页
        PageRequest pageRequest = PageRequest.of(goodsSearchParam.getPage() - 1, goodsSearchParam.getSize());

        nativeQueryBuilder.withPageable(pageRequest);

        //设置查询条件
        nativeQueryBuilder.withQuery(boolbuilder.build()._toQuery());

        //构建查询条件
        NativeQuery query = nativeQueryBuilder.build();
        return query;
    }

    @Override
    public void syncGoodsToES(GoodsDesc goodsDesc) {

        //创建GoodsES对象
        GoodsES goodsES = new GoodsES();
        //封装数据
        goodsES.setId(goodsDesc.getId());
        goodsES.setGoodsName(goodsDesc.getGoodsName());
        goodsES.setCaption(goodsDesc.getCaption());
        goodsES.setPrice(goodsDesc.getPrice());
        goodsES.setHeaderPic(goodsDesc.getHeaderPic());
        goodsES.setBrand(goodsDesc.getBrand().getName());

        //标签集合，用户搜索时候使用
        //创建集合
        List<String> tags = new ArrayList<>();
        tags.add(goodsDesc.getBrand().getName());
        //商品名称
        tags.addAll(this.analyzer(goodsDesc.getGoodsName(), Constant.IK_SMART));
        tags.addAll(this.analyzer(goodsDesc.getCaption(), Constant.IK_SMART));
        //设置标签
        goodsES.setTags(tags);

        //设置分类
        List<String> productTypeNames = new ArrayList<>();
        productTypeNames.add(goodsDesc.getProductType1().getName());
        productTypeNames.add(goodsDesc.getProductType2().getName());
        productTypeNames.add(goodsDesc.getProductType3().getName());
        goodsES.setProductType(productTypeNames);
        //规格数据

        List<Specification> specifications = goodsDesc.getSpecifications();

        //创建Map集合存储，规格数据
        HashMap<String, List<String>> specificationsMap = new HashMap<>();

        if (CollUtil.isNotEmpty(specifications)) {
            //循环规格
            for (Specification specification : specifications) {
                //获取规格名称
                String specName = specification.getSpecName();
                //获取规格选项
                List<SpecificationOption> specificationOptions = specification.getSpecificationOptions();

                //获取规格项
                if (CollUtil.isNotEmpty(specificationOptions)) {
                    //屏幕尺寸->["6.7","7.1","50"]
                    //cpu ->[8g1,8g2,8g3]
                    List<String> options = specificationOptions.stream().map(SpecificationOption::getOptionName).toList();
                    //封装商品规格数据
                    specificationsMap.put(specName, options);
                }

            }
        }
        //设置规格数据
        goodsES.setSpecification(specificationsMap);

        //新增修改ES数据
        goodsEsRepository.save(goodsES);
    }

    /**
     * 分词方法
     *
     * @param text     要分词的内容
     * @param analyzer 分词器
     * @return 分词结果
     */
    @SneakyThrows //抛出异常
    public List<String> analyzer(String text, String analyzer) {

        //创建分词请求对象
        AnalyzeRequest request = AnalyzeRequest.of(fn -> fn.index(Constant.GOODS).analyzer(analyzer).text(text));

        //分词,并获取响应结果
        AnalyzeResponse analyzeResponse = client.indices().analyze(request);

        //处理结果
        List<AnalyzeToken> tokens = analyzeResponse.tokens();

        //获取分词结果内容
        List<String> words = tokens.stream().map(AnalyzeToken::token).toList();

        return words;
    }


    @Override
    public void delete(Long id) {
        goodsEsRepository.deleteById(id);
    }
}
